package com.code2roc.xhood.util;

import com.code2roc.xhood.asm.*;
import com.code2roc.xhood.model.*;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.*;

import java.io.File;
import java.io.FileInputStream;
import java.util.*;

public class ObscureUtil {

    public static ObscureClassDefine doObscureDefineClass(File file, ObscureConfig obscureConfig) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassDefineVisitor cv = new ClassDefineVisitor(cw, obscureConfig);
        cr.accept(cv, 0);
        return new ObscureClassDefine(cw.toByteArray(), cv.isModify(), cv.getModifyMap(), cv.getSourceMap());
    }

    public static byte[] doObscureDefineClassUse(File file, LinkedHashMap<String, String> modifyMap, HashMap<String, String> sourceMap) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassDefineUseVisitor cv = new ClassDefineUseVisitor(cw, modifyMap, sourceMap);
        cr.accept(cv, 0);
        return cw.toByteArray();
    }

    public static byte[] doObscureInvokeClass(File file, LinkedHashMap<String, String> modifyMap) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                return new ClassInvokeAdapter(methodVisitor, modifyMap);
            }
        };
        cr.accept(cv, 0);
        return cw.toByteArray();
    }

    public static ObscurePackageDefine doObscureDefinePackage(File file, ObscureConfig obscureConfig) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        PackageDefineVisitor cv = new PackageDefineVisitor(cw, obscureConfig);
        cr.accept(cv, 0);
        return new ObscurePackageDefine(cw.toByteArray(), cv.isModify(), cv.getModifyMap(), cv.getPackagePath(), cv.getClassPath());
    }

    public static HashMap<String, List<String>> doObscureDefineMethodAnnotion(File file) throws Exception {
        HashMap<String, List<String>> methodAnnotationInfo = new HashMap<>();

        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
            private String className;

            @Override
            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
                this.className = name.replace('/', '.');
                super.visit(version, access, name, signature, superName, interfaces);
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
                String key = className + "." + name + "|" + descriptor;
                methodAnnotationInfo.put(key, new ArrayList<>());
                return new MethodVisitor(Opcodes.ASM9, mv) {
                    @Override
                    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
                        methodAnnotationInfo.get(key).add(descriptor);
                        return super.visitAnnotation(descriptor, visible);
                    }
                };
            }
        };
        cr.accept(cv, 0);
        return methodAnnotationInfo;
    }

    public static ObscureDefine doObscureDefineMethod(File file, ObscureConfig obscureConfig, HashMap<String, List<String>> methodAnnotationMap) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new MethodDefineVisitor(cw, obscureConfig, methodAnnotationMap);
        cr.accept(cv, 0);
        List<String> refreshList = ((MethodDefineVisitor) cv).getRefreshList();
        return new ObscureDefine(cw.toByteArray(), refreshList);
    }

    public static byte[] doObscureInvokeMethod(File file, ObscureConfig obscureConfig, List<String> refreshList) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {

            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                return new MethodInvokeAdapter(methodVisitor, refreshList, obscureConfig.getOutPutPath());
            }
        };
        cr.accept(cv, 0);
        return cw.toByteArray();
    }

    public static ObscureDefine doObscureDefineField(File file, ObscureConfig obscureConfig) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new FieldDefineVisitor(cw, obscureConfig);
        cr.accept(cv, 0);
        List<String> refreshList = ((FieldDefineVisitor) cv).getRefreshList();
        return new ObscureDefine(cw.toByteArray(), refreshList);
    }

    public static byte[] doObscureEnumAnnotationField(File file, LinkedHashMap<String, String> modifyMap) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new FieldEnumAnnotationVisitor(cw, modifyMap);
        cr.accept(cv, 0);
        return cw.toByteArray();
    }

    public static byte[] doObscureInvokeField(File file, ObscureConfig obscureConfig, List<String> refreshList) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                return new FieldInvokeAdapter(methodVisitor, refreshList, obscureConfig.getOutPutPath());
            }
        };
        cr.accept(cv, 0);
        return cw.toByteArray();
    }

    public static byte[] doObscureLocalVariable(File file, ObscureConfig obscureConfig) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
            private String className;
            private String classPackageName;

            @Override
            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
                super.visit(version, access, name, signature, superName, interfaces);
                this.className = name.replace('/', '.');
                List<String> classNameList = new ArrayList<>(Arrays.asList(className.split("\\.")));
                classNameList.remove(classNameList.size() - 1);
                this.classPackageName = StringUtils.join(classNameList, ".");
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                boolean modifyInvoke = true;
                boolean exist = false;
                for (String p : obscureConfig.getPackages()) {
                    if (classPackageName.startsWith(p)) {
                        exist = true;
                        break;
                    }
                }
                if (!exist) {
                    modifyInvoke = false;
                }
                for (String p : obscureConfig.getIgnorePackages()) {
                    if (p.startsWith("*")) {
                        if (classPackageName.endsWith(p.replace("*/", ""))) {
                            modifyInvoke = false;
                            break;
                        }
                    } else {
                        if (p.equals(classPackageName)) {
                            modifyInvoke = false;
                            break;
                        }
                    }
                }
                for (String p : obscureConfig.getIgnoreClasss()) {
                    if (p.startsWith("*")) {
                        if (className.endsWith(p.replace("*", ""))) {
                            modifyInvoke = false;
                            break;
                        }
                    } else {
                        if (p.equals(className)) {
                            modifyInvoke = false;
                            break;
                        }
                    }
                }
                if (modifyInvoke) {
                    return new LocalVariableAdapter(className, methodVisitor);
                } else {
                    return new EmptyAdapter(methodVisitor);
                }
            }
        };
        cr.accept(cv, 0);
        return cw.toByteArray();
    }

    public static byte[] doObscureParamter(File file, ObscureConfig obscureConfig) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
            private String className;
            private String classPackageName;

            @Override
            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
                super.visit(version, access, name, signature, superName, interfaces);
                this.className = name.replace('/', '.');
                List<String> classNameList = new ArrayList<>(Arrays.asList(className.split("\\.")));
                classNameList.remove(classNameList.size() - 1);
                this.classPackageName = StringUtils.join(classNameList, ".");
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                boolean modifyInvoke = true;
                boolean exist = false;
                for (String p : obscureConfig.getPackages()) {
                    if (classPackageName.startsWith(p)) {
                        exist = true;
                        break;
                    }
                }
                if (!exist) {
                    modifyInvoke = false;
                }
                for (String p : obscureConfig.getIgnorePackages()) {
                    if (p.startsWith("*")) {
                        if (classPackageName.endsWith(p.replace("*/", ""))) {
                            modifyInvoke = false;
                            break;
                        }
                    } else {
                        if (p.equals(classPackageName)) {
                            modifyInvoke = false;
                            break;
                        }
                    }
                }
                for (String p : obscureConfig.getIgnoreClasss()) {
                    if (p.startsWith("*")) {
                        if (className.endsWith(p.replace("*", ""))) {
                            modifyInvoke = false;
                            break;
                        }
                    } else {
                        if (p.equals(className)) {
                            modifyInvoke = false;
                            break;
                        }
                    }
                }
                if (modifyInvoke) {
                    return new ParamterAdapter(className, methodVisitor);
                } else {
                    return new EmptyAdapter(methodVisitor);
                }
            }
        };
        cr.accept(cv, 0);
        return cw.toByteArray();
    }

    public static byte[] doObscureConstant(File file, ObscureConfig obscureConfig, String key) throws Exception {
        ClassReader cr = new ClassReader(new FileInputStream(file));
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
            private String className;
            private String classPackageName;

            @Override
            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
                super.visit(version, access, name, signature, superName, interfaces);
                this.className = name.replace('/', '.');
                List<String> classNameList = new ArrayList<>(Arrays.asList(className.split("\\.")));
                classNameList.remove(classNameList.size() - 1);
                this.classPackageName = StringUtils.join(classNameList, ".");
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                boolean modifyInvoke = true;
                boolean exist = false;
                for (String p : obscureConfig.getPackages()) {
                    if (classPackageName.startsWith(p)) {
                        exist = true;
                        break;
                    }
                }
                if (!exist) {
                    modifyInvoke = false;
                }
                for (String p : obscureConfig.getIgnorePackages()) {
                    if (p.startsWith("*")) {
                        if (classPackageName.endsWith(p.replace("*/", ""))) {
                            modifyInvoke = false;
                            break;
                        }
                    } else {
                        if (p.equals(classPackageName)) {
                            modifyInvoke = false;
                            break;
                        }
                    }
                }
                for (String p : obscureConfig.getIgnoreClasss()) {
                    if (p.startsWith("*")) {
                        if (className.endsWith(p.replace("*", ""))) {
                            modifyInvoke = false;
                            break;
                        }
                    } else {
                        if (p.equals(className)) {
                            modifyInvoke = false;
                            break;
                        }
                    }
                }
                if (modifyInvoke) {
                    return new ConstantAdapter(methodVisitor, obscureConfig, key);
                } else {
                    return new EmptyAdapter(methodVisitor);
                }
            }
        };
        cr.accept(cv, 0);
        return cw.toByteArray();
    }

    public static boolean isConstructor(String methodName) {
        return methodName.equals("<init>") || methodName.equals("<clinit>");
    }

    public static byte[] createConstantUtilClassFile(String packageName, String key) {
        String className = "ConstantUtil";

        // 创建 ClassWriter 实例，用于生成类字节码
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

        // 设置类的基本信息
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, packageName.replace(".", "/") + "/" + className, null, "java/lang/Object", null);

        // 生成静态常量 key
        cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "key", "Ljava/lang/String;", null, key).visitEnd();

        // 生成构造方法
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        // 生成 decrypt 方法
        mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "decrypt", "(Ljava/lang/String;)Ljava/lang/String;", null, null);
        mv.visitCode();

        // 在 decrypt 方法中添加代码
        mv.visitLdcInsn("");
        mv.visitVarInsn(Opcodes.ASTORE, 1);

        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Base64", "getDecoder", "()Ljava/util/Base64$Decoder;", false);
        mv.visitVarInsn(Opcodes.ASTORE, 2);

        mv.visitVarInsn(Opcodes.ALOAD, 2);
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/Base64$Decoder", "decode", "(Ljava/lang/String;)[B", false);
        mv.visitVarInsn(Opcodes.ASTORE, 3);

        mv.visitTypeInsn(Opcodes.NEW, "java/security/SecureRandom");
        mv.visitInsn(Opcodes.DUP);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/security/SecureRandom", "<init>", "()V", false);
        mv.visitVarInsn(Opcodes.ASTORE, 4);

        mv.visitTypeInsn(Opcodes.NEW, "javax/crypto/spec/DESKeySpec");
        mv.visitInsn(Opcodes.DUP);
        mv.visitFieldInsn(Opcodes.GETSTATIC, packageName.replace(".", "/") + "/" + className, "key", "Ljava/lang/String;");
        mv.visitLdcInsn("UTF-8");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "getBytes", "(Ljava/lang/String;)[B", false);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "javax/crypto/spec/DESKeySpec", "<init>", "([B)V", false);
        mv.visitVarInsn(Opcodes.ASTORE, 5);

        mv.visitLdcInsn("DES");
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "javax/crypto/SecretKeyFactory", "getInstance", "(Ljava/lang/String;)Ljavax/crypto/SecretKeyFactory;", false);
        mv.visitVarInsn(Opcodes.ASTORE, 6);

        mv.visitVarInsn(Opcodes.ALOAD, 6);
        mv.visitVarInsn(Opcodes.ALOAD, 5);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "javax/crypto/SecretKeyFactory", "generateSecret", "(Ljava/security/spec/KeySpec;)Ljavax/crypto/SecretKey;", false);
        mv.visitVarInsn(Opcodes.ASTORE, 7);

        mv.visitLdcInsn("DES");
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "javax/crypto/Cipher", "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;", false);
        mv.visitVarInsn(Opcodes.ASTORE, 8);

        mv.visitVarInsn(Opcodes.ALOAD, 8);
        mv.visitFieldInsn(Opcodes.GETSTATIC, "javax/crypto/Cipher", "DECRYPT_MODE", "I");
        mv.visitVarInsn(Opcodes.ALOAD, 7);
        mv.visitVarInsn(Opcodes.ALOAD, 4);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "javax/crypto/Cipher", "init", "(ILjava/security/Key;Ljava/security/SecureRandom;)V", false);

        mv.visitVarInsn(Opcodes.ALOAD, 8);
        mv.visitVarInsn(Opcodes.ALOAD, 3);
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "javax/crypto/Cipher", "doFinal", "([B)[B", false);
        mv.visitVarInsn(Opcodes.ASTORE, 9);

        mv.visitTypeInsn(Opcodes.NEW, "java/lang/String");
        mv.visitInsn(Opcodes.DUP);
        mv.visitVarInsn(Opcodes.ALOAD, 9);
        mv.visitLdcInsn("UTF-8");
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/String", "<init>", "([BLjava/lang/String;)V", false);
        mv.visitVarInsn(Opcodes.ASTORE, 1);

        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitInsn(Opcodes.ARETURN);

        mv.visitMaxs(0, 0);
        mv.visitEnd();

        cw.visitEnd();
        return cw.toByteArray();
    }

    public static String getObscureName(String name) {
        String result = "";
        String md5name = CommonUtil.encryptByMD5(name);
        String part1 = md5name.substring(0, 5);
        String part2 = md5name.substring(5, md5name.length() - 1);
        result = CommonUtil.encryptByMD5(part2 + part1);
        return result;
    }

    private static HashMap<ObsureEnum, HashMap<String, String>> obsucreOutMap = new HashMap<>();

    public static String getObscureNameV1(String name, ObsureEnum obsureEuum) {
        if (!obsucreOutMap.containsKey(obsureEuum)) {
            HashMap<String, String> typeMap = new HashMap<>();
            obsucreOutMap.put(obsureEuum, typeMap);
        }
        HashMap<String, String> obsucreMap = obsucreOutMap.get(obsureEuum);
        String obscureName = "";
        String prefix = "";
        String takeName = "";
        int takeLimit = 0;
        switch (obsureEuum) {
            case Package:
                takeLimit = 2;
                prefix = "pk_";
                if (obsucreMap.containsKey(name)) {
                    takeName = obsucreMap.get(name);
                } else {
                    takeName = getTakeName(name, takeLimit, obsucreMap);
                    obsucreMap.put(name, takeName);
                }
                obscureName = prefix + takeName;
                break;
            case Class:
                if (name.contains("$")) {
                    List<String> converClassList = new ArrayList<>();
                    List<String> classList = new ArrayList<>(Arrays.asList(name.split("\\$")));
                    for (String className : classList) {
                        converClassList.add(getObscureNameV1(className,ObsureEnum.Class));
                    }
                    obscureName = StringUtils.join(converClassList, "$");
                } else {
                    takeLimit = 4;
                    prefix = "c_";
                    if (obsucreMap.containsKey(name)) {
                        takeName = obsucreMap.get(name);
                    } else {
                        takeName = getTakeName(name, takeLimit, obsucreMap);
                        obsucreMap.put(name, takeName);
                    }
                    obscureName = prefix + takeName;
                }
                break;
        }
        return obscureName;
    }

    private static HashMap<String, HashMap<ObsureEnum, HashMap<String, String>>> obsucreInnerMap = new HashMap<>();

    public static String getObscureNameV2(String className, String name, ObsureEnum obsureEuum) {
        if (!obsucreInnerMap.containsKey(className)) {
            HashMap<ObsureEnum, HashMap<String, String>> classMap = new HashMap<>();
            obsucreInnerMap.put(className, classMap);
        }
        HashMap<ObsureEnum, HashMap<String, String>> classMap = obsucreInnerMap.get(className);
        if (!classMap.containsKey(obsureEuum)) {
            HashMap<String, String> typeMap = new HashMap<>();
            classMap.put(obsureEuum, typeMap);
        }
        HashMap<String, String> typeMap = classMap.get(obsureEuum);
        String prefix = "";
        switch (obsureEuum) {
            case LocalVariable:
                prefix = "l_";
                break;
            case Paramter:
                prefix = "p_";
                break;
        }
        String takeName = "";
        if (typeMap.containsKey(name)) {
            takeName = typeMap.get(name);
        } else {
            takeName = getTakeName(name, 2, typeMap);
            typeMap.put(name, takeName);
        }
        return prefix + takeName;
    }

    private static HashMap<ObsureEnum, ClassMapping> obsucreMiddleMap = new HashMap<>();

    public static String getObscureNameV3(String className, String name, ObsureEnum obsureEuum) {
        if (!obsucreMiddleMap.containsKey(obsureEuum)) {
            ClassMapping mapping = new ClassMapping();
            obsucreMiddleMap.put(obsureEuum, mapping);
        }
        ClassMapping mapping  = obsucreMiddleMap.get(obsureEuum);
        String prefix = "";
        switch (obsureEuum) {
            case Method:
                prefix = "m_";
                break;
            case Field:
                prefix = "f_";
                break;
        }
        String takeName = "";
        if (mapping.check(className,name)) {
            takeName = mapping.get(className,name);
        } else {
            takeName = getTakeName(name, 2, mapping.getObsucreMap(className));
            mapping.put(className,name, takeName);
        }
        return prefix + takeName;
    }

    public static String getTakeName(String name, int takeLimit, HashMap<String, String> typeMap) {
        String obscureName = getObscureName(name);
        if (takeLimit <= 16) {
            obscureName = obscureName.substring(8, 24);
        }
        String takeName = obscureName.substring(0, takeLimit);
        if (typeMap.containsValue(takeName)) {
            takeName = getTakeName(name, takeLimit + 1, typeMap);
        }
        return takeName;
    }

    public static void findAllParentMethod(String outPutPath, String methodName, String methodDes, String superClassName, HashMap paramMap) {
        if (!superClassName.equals("java.lang.Object")) {
            try {
                String filePath = outPutPath + File.separator + "classes" + File.separator + superClassName.replace(".", File.separator) + ".class";
                File file = new File(filePath);
                if (file.exists()) {
                    ClassReader cr = new ClassReader(new FileInputStream(file));
                    ClassVisitor cv = new ClassVisitor(Opcodes.ASM9) {
                        @Override
                        public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                            if (name.equals(methodName) && descriptor.equals(methodDes)) {
                                paramMap.put(superClassName + "." + methodName, superClassName);
                            }
                            MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
                            return new EmptyAdapter(methodVisitor);
                        }
                    };
                    cr.accept(cv, 0);
                    String itemSuperName = cr.getSuperName().replace("/", ".");
                    findAllParentMethod(outPutPath, methodName, methodDes, itemSuperName, paramMap);
                    String[] interfaceList = cr.getInterfaces();
                    if (interfaceList != null) {
                        for (String item : interfaceList) {
                            String itemIterfaceName = item.replace('/', '.');
                            findAllParentMethod(outPutPath, methodName, methodDes, itemIterfaceName, paramMap);
                        }
                    }
                } else {
                    paramMap.put(superClassName + "." + methodName, superClassName);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void findAllParentField(String outPutPath, String fieldName, String fieldDes, String superClassName, HashMap paramMap) {
        if (!superClassName.equals("java.lang.Object")) {
            try {
                String filePath = outPutPath + File.separator + "classes" + File.separator + superClassName.replace(".", File.separator) + ".class";
                File file = new File(filePath);
                if (file.exists()) {
                    ClassReader cr = new ClassReader(new FileInputStream(file));
                    ClassVisitor cv = new ClassVisitor(Opcodes.ASM9) {
                        @Override
                        public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
                            if (name.equals(fieldName) && descriptor.equals(fieldDes)) {
                                paramMap.put(superClassName + "." + fieldName, superClassName);
                            }
                            return super.visitField(access, name, descriptor, signature, value);
                        }
                    };
                    cr.accept(cv, 0);
                    String itemSuperName = cr.getSuperName().replace("/", ".");
                    findAllParentField(outPutPath, fieldName, fieldDes, itemSuperName, paramMap);
                    String[] interfaceList = cr.getInterfaces();
                    if (interfaceList != null) {
                        for (String item : interfaceList) {
                            String itemIterfaceName = item.replace('/', '.');
                            findAllParentField(outPutPath, fieldName, fieldDes, itemIterfaceName, paramMap);
                        }
                    }
                } else {
                    paramMap.put(superClassName + "." + fieldName, superClassName);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static ClassParentInfo getClassExtendAndInterfaceInfo(String outPutPath, String className) {
        String filePath = outPutPath + File.separator + "classes" + File.separator + className.replace(".", File.separator) + ".class";
        ClassParentInfo info = new ClassParentInfo();
        try {
            File file = new File(filePath);
            if (file.exists()) {
                ClassReader cr = new ClassReader(new FileInputStream(file));
                ClassWriter cw = new ClassWriter(cr, 0);
                ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {

                };
                cr.accept(cv, 0);
                String superName = cr.getSuperName().replace('/', '.');
                List<String> interfaceList = new ArrayList<>();
                for (String item : cr.getInterfaces()) {
                    interfaceList.add(item.replace('/', '.'));
                }
                info.setSuperName(superName);
                info.setInterfaceList(interfaceList);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return info;
    }
}
